from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button

from kivy.uix.scatter import Scatter
from kivy.uix.image import Image

from kivy.uix.boxlayout import BoxLayout

from kivy.properties import ObjectProperty,  NumericProperty, StringProperty


from kivy.lang import Builder


Builder.load_string("""
<ImageCropWidget>:
    scatter: _scatter
    rect: _rect
    image: _image
    canvas.after:
        Color:
            rgba: [0.8,0.8,0.8,0.7]
        Rectangle:
            pos: self.pos
            size: (self.xmin, self.height)
        Rectangle:
            pos: self.pos
            size: (self.width, self.ymin)
        Rectangle:
            pos: (self.x , self.y+self.ymax)
            size: (self.width, self.height-self.ymax)
        Rectangle:
            pos: (self.x+self.xmax , self.y)
            size: (self.width-self.xmax, self.width)
        Color:
            rgba: [1,0,0,1]
        Line:
            width: 2
            points: [self.x+self.xmin,self.y+self.ymin, self.x+self.xmin,self.y+self.ymax,  self.x+self.xmax,self.y+self.ymax,  self.x+self.xmax, self.y+self.ymin]
            close: True
           
    Scatter:
        id: _scatter
        pos: root.pos
        size: root.size        
        #do_rotation: False
        Image:
            id: _image
            source: root.img_src
            size: _scatter.size
    Widget:
        id: _rect
        size: (root.width/3, root.height/3)
        background_color: [1.,1.,1.,0.80]

<ImageEditor>:
    orientation: 'vertical'
    buttonBox: _buttonBox
    cropW: _cropW
    ImageCropWidget:
        id: _cropW
        img_src: root.img_src
    BoxLayout:    
        id: _buttonBox
        size_hint: (1,None)
        orientation: 'horizontal'
""")



class ImageCropWidget(Widget):
    """Widget containing an image to be cropped/rotated/scaled.
    The kv description defines 'cropping lines'.
    It reacts to
      - mouse wheel by zooming/unzooming the image
      - touch move by either moving the image or a cropping line.
    """
    scatter = ObjectProperty(None)
    image = ObjectProperty(None)    
    rect = ObjectProperty(None)
    xmin = NumericProperty(20)
    xmax = NumericProperty(40)    
    ymin = NumericProperty(20)
    ymax = NumericProperty(40)    

    img_src = StringProperty(None)
    
    def __init__(self, **kargs):
        super(ImageCropWidget, self).__init__(**kargs)
        self.bind( size = self.rescale_rec)
        self.oldsize = tuple(self.size)
        self.hideLimits()
        self._resizing = []
        self.defaultLimits()
        
    def defaultLimits(self):
        self.xmin = self.width/6.
        self.xmax = self.width - self.xmin
        self.ymin = self.width/6.
        self.ymax = self.height - self.ymin

    def hideLimits(self):
        self.xmin = 0
        self.xmax = self.width
        self.ymin = 0
        self.ymax = self.height

        
    def rescale_rec(self,*l):
        o_w, o_h = self.oldsize
        if o_w==0 or o_h==0:
            self.defaultLimits()
            return
        f_w = float(self.size[0])/o_w
        f_h = float(self.size[1])/o_h
        self.xmin *= f_w
        self.ymin *= f_h
        self.xmax *= f_w
        self.ymax *= f_h
        self.oldsize = tuple(self.size)


    def on_touch_down(self, touch):
        if not self.collide_point(*touch.pos):            
            super(ImageCropWidget,self).on_touch_down(touch)
        elif touch.button in( 'scrollup', 'scrolldown'):
            if touch.button == 'scrollup':
                self.scatter.scale *=1.1
            else:
                self.scatter.scale /=1.1
        else:
            # find which line to move : pick the ones within a widow of size 5 of the touch.pos
            f = lambda x0 ,x1 : abs(x0-x1)<5
            x, y = touch.pos
            # normalize in widget coordinate
            x -= self.x
            y -= self.y 
            self._resizing = [] # will contain the function modifying the line positions
            if f(self.xmin,x):
                self._resizing += [self.set_xmin]
            elif f(self.xmax,x):
                self._resizing += [self.set_xmax]
            if f(self.ymin,y):
                self._resizing += [self.set_ymin]
            elif f(self.ymax,y):
                self._resizing += [self.set_ymax]
            if self._resizing==[]:
                self.scatter.dispatch('on_touch_down',touch)
        return True

    def set_xmin(self,x,y):
        self.xmin = x -self.x
    def set_xmax(self,x,y):
        self.xmax = x - self.x
    def set_ymin(self,x,y):
        self.ymin = y- self.y
    def set_ymax(self,x,y):
        self.ymax = y- self.y

    def on_touch_move(self, touch):
        #self.debug(" motion ",self.header._start_resize)
        if self._resizing != [] :
            for f in self._resizing:
                f(*touch.pos)
        else:
            super(ImageCropWidget,self).on_touch_move(touch)
        return True
    
    def on_touch_up(self, touch):
        if self._resizing == [] :
            super(ImageCropWidget,self).on_touch_up(touch)
        return True


    def reset(self):
        self.scatter.rotation = 0
        self.scatter.scale = 1.
        self.scatter.pos = self.pos
        self.hideLimits()

class ImageEditor(BoxLayout):
    """An  ImageCropWidget together with a button bar """
    cropW = ObjectProperty(None)
    buttonBox = ObjectProperty(None)
    ButtonClass= ObjectProperty(Button)

    img_src = StringProperty(None)

    
    def __init__(self, **kargs):
        super(ImageEditor, self).__init__(**kargs)

        def _dipatcher(s):
            def f(*l):
                self.dispatch(s)
            return f
        
        BC  = self.ButtonClass        
        cancelB = BC(text='cancel')        
        cancelB.bind( on_release = _dipatcher('on_canceled') )
        self.buttonBox.add_widget( cancelB)
        cb = BC(text='Crop')
        cb.bind( on_press = self.crop_pressed)
        self.buttonBox.add_widget( cb  )
        rb = BC(text='Rotate')
        rb.bind( on_press = self.rotate_pressed)
        self.buttonBox.add_widget( rb  )
        okb = BC(text='Ok')
        #okb.bind( on_release = self.save_image)
        okb.bind( on_release = _dipatcher('on_validated'))
        
        self.buttonBox.add_widget( okb   )

        self.register_event_type('on_canceled')
        self.register_event_type('on_validated')

    def crop_pressed(self,*l):
        self.cropW.defaultLimits()
        pass

    def rotate_pressed(self,*l):
        self.cropW.scatter.rotation -=90.

    def on_validated(self, *l):
        pass
    def on_canceled(self, *l):
        #self.cropW.reset()
        pass
        
    def save_image(self , new_name='',*l):        
        from PIL import Image
        im = Image.open(self.img_src)
        scatter = self.cropW.scatter
        cropW = self.cropW

        # get the exact ratio, as seen on screen :
        f_window = cropW.image.norm_image_size[0]/float(im.size[0])
        f_window *= scatter.scale

        # rotate as on screent
        im=im.rotate( scatter.rotation)


        # get the image position on screen, within the scatter widget
        size = int(im.size[0]*f_window) , int(im.size[1]*f_window)
        xmin_im = scatter.center_x -size[0]/2 - cropW.x 
        xmax_im = xmin_im + size[0]
        ymin_im = scatter.center_y -size[1]/2 - cropW.y 
        ymax_im = ymin_im + size[1]

        # resize the image as on screen 
        im=im.resize( size, Image.ANTIALIAS )

        # compute crop coordinates (translating from on screen limits position)
        xmin = int(cropW.xmin-xmin_im) 
        xmax = int(cropW.xmax-xmin_im) 
        ymax = int( ymax_im -  cropW.ymax ) 
        ymin = int( ymax_im -  cropW.ymin ) 

        # crop : left, upper, right, and lower
        im=im.crop( (xmin, ymax, xmax, ymin) )

        if new_name == '':
            name_fields = self.img_src.split('.')
            ext = name_fields[-1]
            new_name = '.'.join(name_fields[:-1])+'_edited.'+ext
            print new_name
        im.save( new_name)
        #im.show()
        pass

from kivy.factory import Factory
Factory.register("ImageEditor",ImageEditor)
Factory.register("ImageCropWidget",ImageCropWidget)

## 


if __name__ == '__main__':
    """Test : python miniimageeditor.py image.jpeg"""
    import sys
    print sys.argv[1]
    cw = ImageEditor(img_src=    sys.argv[1]    )

    class MyApp(App):
        def build(self):
            return cw


    ap=MyApp()

    ap.run()





